package com.docmgmt.mvp.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.docmgmt.mvp.dto.PagedResult;
import com.docmgmt.mvp.dto.RecycleBinItemVO;
import com.docmgmt.mvp.dto.Result;
import com.docmgmt.mvp.entity.FileEntity;
import com.docmgmt.mvp.entity.FolderEntity;
import com.docmgmt.mvp.exception.BusinessException;
import com.docmgmt.mvp.exception.NotFoundException;
import com.docmgmt.mvp.exception.UnauthorizedException;
import com.docmgmt.mvp.mapper.FileMapper;
import com.docmgmt.mvp.mapper.FolderMapper;
import com.docmgmt.mvp.service.PermissionService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Recycle Bin Controller
 *
 * @author Dora
 */
@Slf4j
@RestController
@RequestMapping("/api/v1/recycle-bin")
@RequiredArgsConstructor
public class RecycleBinController {

    private final FileMapper fileMapper;
    private final FolderMapper folderMapper;
    private final PermissionService permissionService;

    /**
     * 6.1 List Recycle Bin Items
     *
     * GET /api/v1/recycle-bin
     */
    @GetMapping
    public Result<PagedResult<RecycleBinItemVO>> listRecycleBin(
            @RequestParam(required = false) String type,
            @RequestParam(defaultValue = "1") Integer page,
            @RequestParam(defaultValue = "20") Integer pageSize) {
        Long userId = getCurrentUserId();

        log.info("GET /api/v1/recycle-bin - userId={}, type={}", userId, type);

        List<RecycleBinItemVO> items = new ArrayList<>();

        // Query deleted files
        if (type == null || "file".equals(type)) {
            QueryWrapper<FileEntity> fileQuery = new QueryWrapper<>();
            fileQuery.eq("is_deleted", true)
                    .eq("uploaded_by", userId);

            List<FileEntity> deletedFiles = fileMapper.selectList(fileQuery);
            items.addAll(deletedFiles.stream()
                    .map(this::fileToRecycleBinItem)
                    .collect(Collectors.toList()));
        }

        // Query deleted folders
        if (type == null || "folder".equals(type)) {
            QueryWrapper<FolderEntity> folderQuery = new QueryWrapper<>();
            folderQuery.eq("is_deleted", true)
                    .eq("creator_id", userId);

            List<FolderEntity> deletedFolders = folderMapper.selectList(folderQuery);
            items.addAll(deletedFolders.stream()
                    .map(this::folderToRecycleBinItem)
                    .collect(Collectors.toList()));
        }

        // Sort by deletion time (most recent first)
        items.sort(Comparator.comparing(RecycleBinItemVO::getDeletedAt).reversed());

        // Manual pagination
        int total = items.size();
        int start = (page - 1) * pageSize;
        int end = Math.min(start + pageSize, total);

        List<RecycleBinItemVO> pageItems = start < total ?
                items.subList(start, end) : new ArrayList<>();

        PagedResult<RecycleBinItemVO> pagedResult = PagedResult.<RecycleBinItemVO>builder()
                .items(pageItems)
                .pagination(PagedResult.Pagination.builder()
                        .page(page)
                        .pageSize(pageSize)
                        .total((long) total)
                        .totalPages((int) Math.ceil((double) total / pageSize))
                        .build())
                .build();

        return Result.success("Query successful", pagedResult);
    }

    /**
     * 6.2 Restore from Recycle Bin
     *
     * POST /api/v1/recycle-bin/{type}/{id}/restore
     */
    @PostMapping("/{type}/{id}/restore")
    @Transactional(rollbackFor = Exception.class)
    public Result<Void> restoreItem(
            @PathVariable String type,
            @PathVariable Long id) {
        Long userId = getCurrentUserId();

        log.info("POST /api/v1/recycle-bin/{}/{}/restore - userId={}", type, id, userId);

        if ("file".equals(type)) {
            FileEntity file = fileMapper.selectById(id);
            if (file == null) {
                throw new NotFoundException("File not found");
            }
            if (!file.getUploadedBy().equals(userId)) {
                throw new UnauthorizedException("Not the owner of this file");
            }
            if (!file.getIsDeleted()) {
                throw new BusinessException("File is not in recycle bin");
            }

            // Restore file
            file.setIsDeleted(false);
            file.setDeletedAt(null);
            file.setDeletedBy(null);
            file.setUpdatedAt(LocalDateTime.now());
            fileMapper.updateById(file);

            log.info("File restored: id={}, name={}", id, file.getFileName());
            return Result.success("File restored successfully");

        } else if ("folder".equals(type)) {
            FolderEntity folder = folderMapper.selectById(id);
            if (folder == null) {
                throw new NotFoundException("Folder not found");
            }
            if (!folder.getCreatorId().equals(userId)) {
                throw new UnauthorizedException("Not the owner of this folder");
            }
            if (!folder.getIsDeleted()) {
                throw new BusinessException("Folder is not in recycle bin");
            }

            // Restore folder
            folder.setIsDeleted(false);
            folder.setDeletedAt(null);
            folder.setDeletedBy(null);
            folder.setUpdatedAt(LocalDateTime.now());
            folderMapper.updateById(folder);

            log.info("Folder restored: id={}, name={}", id, folder.getName());
            return Result.success("Folder restored successfully");

        } else {
            throw new BusinessException("Invalid type: " + type);
        }
    }

    /**
     * 6.3 Permanently Delete
     *
     * DELETE /api/v1/recycle-bin/{type}/{id}
     */
    @DeleteMapping("/{type}/{id}")
    @Transactional(rollbackFor = Exception.class)
    public Result<Void> permanentlyDelete(
            @PathVariable String type,
            @PathVariable Long id) {
        Long userId = getCurrentUserId();

        log.info("DELETE /api/v1/recycle-bin/{}/{} - userId={}", type, id, userId);

        if ("file".equals(type)) {
            FileEntity file = fileMapper.selectById(id);
            if (file == null) {
                throw new NotFoundException("File not found");
            }
            if (!file.getUploadedBy().equals(userId)) {
                throw new UnauthorizedException("Not the owner of this file");
            }
            if (!file.getIsDeleted()) {
                throw new BusinessException("File is not in recycle bin");
            }

            // Permanently delete file
            fileMapper.deleteById(id);

            log.warn("File permanently deleted: id={}, name={}", id, file.getFileName());
            return Result.success("File permanently deleted");

        } else if ("folder".equals(type)) {
            FolderEntity folder = folderMapper.selectById(id);
            if (folder == null) {
                throw new NotFoundException("Folder not found");
            }
            if (!folder.getCreatorId().equals(userId)) {
                throw new UnauthorizedException("Not the owner of this folder");
            }
            if (!folder.getIsDeleted()) {
                throw new BusinessException("Folder is not in recycle bin");
            }

            // Permanently delete folder
            folderMapper.deleteById(id);

            log.warn("Folder permanently deleted: id={}, name={}", id, folder.getName());
            return Result.success("Folder permanently deleted");

        } else {
            throw new BusinessException("Invalid type: " + type);
        }
    }

    /**
     * 6.4 Empty Recycle Bin
     *
     * DELETE /api/v1/recycle-bin/empty
     */
    @DeleteMapping("/empty")
    @Transactional(rollbackFor = Exception.class)
    public Result<Void> emptyRecycleBin() {
        Long userId = getCurrentUserId();

        log.info("DELETE /api/v1/recycle-bin/empty - userId={}", userId);

        // Delete all user's deleted files
        QueryWrapper<FileEntity> fileQuery = new QueryWrapper<>();
        fileQuery.eq("is_deleted", true)
                .eq("uploaded_by", userId);
        int deletedFiles = fileMapper.delete(fileQuery);

        // Delete all user's deleted folders
        QueryWrapper<FolderEntity> folderQuery = new QueryWrapper<>();
        folderQuery.eq("is_deleted", true)
                .eq("creator_id", userId);
        int deletedFolders = folderMapper.delete(folderQuery);

        log.warn("Recycle bin emptied: userId={}, files={}, folders={}",
                userId, deletedFiles, deletedFolders);

        return Result.success("Recycle bin emptied successfully (deleted " +
                deletedFiles + " files and " + deletedFolders + " folders)");
    }

    /**
     * Convert FileEntity to RecycleBinItemVO
     */
    private RecycleBinItemVO fileToRecycleBinItem(FileEntity file) {
        return RecycleBinItemVO.builder()
                .type("file")
                .id(file.getId())
                .name(file.getFileName())
                .path(file.getFilePath())
                .size(file.getFileSize())
                .sizeDisplay(formatFileSize(file.getFileSize()))
                .extension(file.getFileExtension())
                .mimeType(file.getMimeType())
                .deletedAt(file.getDeletedAt() != null ?
                        file.getDeletedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) : null)
                .deletedBy(file.getDeletedBy())
                .deletedByName("Unknown User")
                .originalPath(file.getFilePath())
                .build();
    }

    /**
     * Convert FolderEntity to RecycleBinItemVO
     */
    private RecycleBinItemVO folderToRecycleBinItem(FolderEntity folder) {
        return RecycleBinItemVO.builder()
                .type("folder")
                .id(folder.getId())
                .name(folder.getName())
                .path(folder.getPath())
                .deletedAt(folder.getDeletedAt() != null ?
                        folder.getDeletedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) : null)
                .deletedBy(folder.getDeletedBy())
                .deletedByName("Unknown User")
                .originalPath(folder.getPath())
                .build();
    }

    /**
     * Format file size
     */
    private String formatFileSize(Long size) {
        if (size == null || size == 0) {
            return "0 B";
        }
        String[] units = {"B", "KB", "MB", "GB", "TB"};
        int unitIndex = 0;
        double sizeInUnit = size.doubleValue();

        while (sizeInUnit >= 1024 && unitIndex < units.length - 1) {
            sizeInUnit /= 1024;
            unitIndex++;
        }

        return String.format("%.2f %s", sizeInUnit, units[unitIndex]);
    }

    /**
     * Get current user ID from Spring Security context
     */
    private Long getCurrentUserId() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            throw new UnauthorizedException("Not logged in or authentication expired");
        }

        Object principal = authentication.getPrincipal();
        if (principal instanceof Long) {
            return (Long) principal;
        }

        throw new UnauthorizedException("Invalid authentication information");
    }
}
